type UnPromise<T> = T extends Promise<infer R> ? R : T

export function debounce<T extends (...args: any[]) => any>(
  func: T,
  timeout = 0
): (...args: Parameters<T>) => Promise<UnPromise<ReturnType<T>>> {
  let timer: NodeJS.Timeout
  let pro: Promise<UnPromise<ReturnType<T>>> | undefined
  let resolve: ((value: ReturnType<T>) => void) | undefined
  let reject: ((ex: any) => void) | undefined
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(async () => {
      if (pro) {
        const _resolve = resolve!
        const _reject = reject!
        pro = resolve = reject = undefined
        try {
          _resolve(await func(...args))
        } catch (ex) {
          _reject(ex)
        }
      }
    }, timeout)
    if (!pro) {
      pro = new Promise((a, b) => {
        resolve = a
        reject = b
      })
    }
    return pro
  }
}
